package gat.application.tg.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.agency20221216.models.GetCreditInfoResponseBody;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bitsun.core.common.exception.AppException;
import com.bitsun.core.common.exception.ErrorCode;
import com.bitsun.core.common.persistence.BasePService;
import com.bitsun.core.common.persistence.IPService;
import com.bitsun.core.common.persistence.Pager;
import com.bitsun.core.common.persistence.QueryParamUtils;
import com.google.common.base.CaseFormat;
import gat.application.tg.AccountDbService;
import gat.common.config.AliConfig;
import gat.common.config.TencentConfig;
import gat.common.util.*;
import gat.convertor.tg.AccountDbReqDtoConvertor;
import gat.convertor.tg.AccountDbResDtoConvertor;
import gat.domain.model.tg.ExcelAccountDb;
import gat.domain.model.tg.ExcelOrderDb;
import gat.domain.service.thirdParty.tp.TpFactory;
import gat.domain.service.thirdParty.tp.TpService;
import gat.dto.req.tg.AccountDbReqDto;
import gat.dto.req.tg.AccountDbReqParams;
import gat.dto.req.tg.BindAccountParams;
import gat.dto.req.tg.FreezeAccountParams;
import gat.dto.res.tg.AccountDbResDto;
import gat.infrastructure.persistence.dao.backgroundUser.BackgroundUserMapper;
import gat.infrastructure.persistence.dao.tg.AccountDbMapper;
import gat.infrastructure.persistence.dao.tg.UserBotMapper;
import gat.infrastructure.persistence.po.backgroundUser.BackgroundUserPo;
import gat.infrastructure.persistence.po.tg.AccountDbPo;
import gat.infrastructure.persistence.po.tg.TgBotPo;
import gat.infrastructure.persistence.po.tg.UserBotPo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * 示例表，应用（业务编排）层实现
 * @author: yanshukeji-java
 * @email: yanshukeji-java
 */
@Slf4j
@Service("accountDbAppService")
@Transactional(rollbackFor = Exception.class)
public class AccountDbServiceImpl implements AccountDbService {

    @Autowired
    private AccountDbReqDtoConvertor accountDbReqDtoConvertor;

    @Autowired
    private AccountDbResDtoConvertor accountDbResDtoConvertor;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private TpFactory tpFactory;

    @Autowired
    private AliConfig aliConfig;

    @Autowired
    private TencentConfig tencentConfig;

    @Autowired
    private AliUtil aliUtil;

    @Autowired
    private TencentUtils tencentUtils;

    @Autowired
    private BackgroundUserMapper backgroundUserMapper;

    @Autowired
    private UserBotMapper userBotMapper;


    private IPService<AccountDbPo> accountDbPoService;

    @SuppressWarnings("SpringJavaAutowiringInspection")
    public AccountDbServiceImpl(AccountDbMapper accountDbMapper) {
        this.accountDbPoService = new BasePService(accountDbMapper);
    }



    @Override
    public AccountDbResDto save(AccountDbReqDto reqDto) {
        AccountDbPo po = accountDbReqDtoConvertor.dto2Po(reqDto);
        accountDbPoService.save(po);
        AccountDbResDto resDto = accountDbResDtoConvertor.po2Dto(po);
        return resDto;
    }

    @Override
    public boolean deleteByIds(String ids) {
        try {
            List<Long> idLongList =
            Arrays.asList(ids.split(",")).stream().map(s -> NumberUtils.createLong(s.trim())).collect(Collectors.toList());

            for (Long id : idLongList) {
                AccountDbPo accountDbPo = accountDbPoService.getById(id);
                if (accountDbPo.getStatus() != 0) {
                    throw new AppException("当前账号为已分配状态，无法删除");
                }
            }

            return accountDbPoService.removeByIds(idLongList);
        } catch (Exception e) {
            throw new AppException("参数错误：" + ids, ErrorCode.pc("417"), e);
        }
     }

    @Override
    public Pager<AccountDbResDto> doPager(Map<String, Object> params) {
        Pager<AccountDbPo> poPager = accountDbPoService.queryPage(params, AccountDbPo.class);
        Pager<AccountDbResDto> resDtoPager = accountDbResDtoConvertor.convertPoPager2ResDtoPager(poPager);
        return resDtoPager;
    }

    @Override
    public AccountDbResDto selectOne(Long id) {
        AccountDbPo po = accountDbPoService.getById(id);
        AccountDbResDto resDto = accountDbResDtoConvertor.po2Dto(po);
        return resDto;
    }

    @Override
    public AccountDbResDto selectOne(Map<String, Object> params) {
        QueryWrapper queryWrapper = QueryParamUtils.queryWrapper4eq(AccountDbPo::new, params);
        queryWrapper.last(" limit 1");
        AccountDbPo po = accountDbPoService.getOne(queryWrapper);
        AccountDbResDto resDto = accountDbResDtoConvertor.po2Dto(po);
        return resDto;
    }

    @Override
    public boolean updateProps(Long id, Map<String, Object> params) {
        UpdateWrapper<AccountDbPo> updateWrapper = QueryParamUtils.updateWrapper4Map(AccountDbPo::new, id, params);
        return accountDbPoService.update(new AccountDbPo(), updateWrapper);
        }

    @Override
    public boolean updateProps(Long id, AccountDbReqDto reqDto) {
        AccountDbPo po = accountDbReqDtoConvertor.dto2Po(reqDto);
        po.setId(id);
        return accountDbPoService.updateById(po);
    }


    @Override
    public boolean updateAllProps(Long id, AccountDbReqDto reqDto) {
        UpdateWrapper<AccountDbPo> updateWrapper = Wrappers.update();
        updateWrapper.eq("id", id);
        Field[] fields = AccountDbReqDto.class.getDeclaredFields();
        Arrays.stream(fields).filter(f -> !Modifier.isStatic(f.getModifiers())).forEach(field -> {
        try {
        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), AccountDbReqDto.class);
        Method getMethod = propertyDescriptor.getReadMethod();
        String fileNameCamel = getMethod.getName().substring(3);
        String fileNameUnderline = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fileNameCamel);
        updateWrapper.set(fileNameUnderline, getMethod.invoke(reqDto));
        } catch (Exception ex) {
        log.warn("属性不存在get方法："+field.getName(),ex);
        }
        });
        return accountDbPoService.update(new AccountDbPo(), updateWrapper);
    }

    @Override
    public Pager<AccountDbResDto> queryAccountDb(AccountDbReqParams params) {
        String username = CurrentThread.getCurrentUser().getUsername();
        if (!StringUtils.equals(username, "admin") && StringUtils.isEmpty(params.getBotId())) {
            throw new AppException("参数错误：机器人id不能为空");
        }
        if (StringUtils.isNotEmpty(params.getStartTime()) ) {
            params.setStartTime(params.getStartTime() + " 00:00:01");
        }
        if (StringUtils.isNotEmpty(params.getEndTime())) {
            params.setEndTime(params.getEndTime() + " 23:59:59");
        }

        IPage<AccountDbPo> iPage =
                accountDbPoService.page(new Page<>(params.getCurrentPage(), params.getPageSize()),
                        new LambdaQueryWrapper<AccountDbPo>()
                                .ne(StringUtils.isNotBlank(params.getStartTime()), AccountDbPo::getCreateTime, params.getStartTime())
                                .le(StringUtils.isNotBlank(params.getEndTime()), AccountDbPo::getCreateTime, params.getEndTime())
                                .eq(StringUtils.isNotBlank(params.getAccountId()), AccountDbPo::getAccountId, params.getAccountId())
                                .eq(StringUtils.isNotBlank(params.getAccountSupplier()), AccountDbPo::getAccountSupplier, params.getAccountSupplier())
                                .eq(StringUtils.isNotBlank(params.getAccountMail()), AccountDbPo::getAccountMail, params.getAccountMail())
                                .eq(ObjectUtil.isNotEmpty(params.getStatus()), AccountDbPo::getStatus, params.getStatus())
                                .eq(StringUtils.isNotBlank(params.getTgUserId()), AccountDbPo::getTgUserId, params.getTgUserId())
                                .eq(StringUtils.isNotBlank(params.getOrderNo()), AccountDbPo::getOrderNo, params.getOrderNo())
                                .eq(StringUtils.isNotBlank(params.getAccountType()), AccountDbPo::getAccountType, params.getAccountType())
                                .eq(StringUtils.isNotBlank(params.getBotId()), AccountDbPo::getBotId, params.getBotId())
                                .eq(StringUtils.isNotBlank(params.getSource()), AccountDbPo::getSource, params.getSource())
                                .orderByDesc(AccountDbPo::getCreateTime)

                );
        return accountDbResDtoConvertor.convertPoPager2ResDtoPager(
                new Pager<>(iPage.getRecords(), iPage.getTotal(), params.getPageSize(), iPage.getCurrent()));
    }

    @Override
    public void importAccount(MultipartFile file) {
        // 加redis锁 系统仅支持处理一个excel
        RLock lock = redissonClient.getLock("importAccount");
        boolean locked = false;
        try {
            locked = lock.tryLock();
            if (locked) {
                List<ExcelAccountDb> excelAccountDbs = ExcelUtils.parseExcelToEntityList(file, ExcelAccountDb.class, true, 0);
                String username = CurrentThread.getCurrentUser().getUsername();
                String botId = "";
                if (!StringUtils.equals(username, "admin")) {
                    UserBotPo userBotPo = userBotMapper.selectOne(new LambdaQueryWrapper<UserBotPo>()
                            .eq(UserBotPo::getUserCode, CurrentThread.getCurrentUser().getUserCode())
                    );
                    if (userBotPo != null) {
                        botId = userBotPo.getBotId();
                    }
                }

                String finalBotId = botId;
                excelAccountDbs.forEach(excelAccountDb -> {
                    if (excelAccountDb.getId()  != null && StringUtils.isNotBlank(excelAccountDb.getCloudProvider())) {
                        AccountDbPo accountDb1 = accountDbPoService.getOne(new LambdaQueryWrapper<AccountDbPo>()
                                .eq(AccountDbPo::getAccountId, excelAccountDb.getAccountId())
                                .eq(AccountDbPo::getAccountSupplier, excelAccountDb.getCloudProvider().equals("阿里云") ? "ali" : "tencent")
                        );
                        if (accountDb1 == null) {
                            AccountDbPo accountDb = new AccountDbPo();
                            accountDb.setAccountId(excelAccountDb.getAccountId());
                            accountDb.setAccountSupplier(excelAccountDb.getCloudProvider().equals("阿里云") ? "ali" : "tencent") ;
                            accountDb.setAccountMail(excelAccountDb.getEmail());
                            accountDb.setAccountPassword(excelAccountDb.getCloudPassword());
                            accountDb.setMailPassword(excelAccountDb.getEmailPassword());
                            accountDb.setStatus(0);
                            accountDb.setRemark(excelAccountDb.getRemark());
                            accountDb.setSource("excel");
                            accountDb.setCreateTime(LocalDateTime.now());

                            accountDb.setMailLoginUrl(excelAccountDb.getEmailLoginUrl());
                            accountDb.setWebsiteUrl(excelAccountDb.getWebsite());

                            accountDb.setAccountType(excelAccountDb.getAccountType());
                            if (StringUtils.isNotBlank(finalBotId)) {
                                accountDb.setBotId(finalBotId);
                            }
                            accountDbPoService.save(accountDb);
                        }
                    }
                });
            }
        } catch (Exception e) {
            throw new AppException("文件解析失败");
        } finally {
            if (locked) {
                lock.unlock();
            }
        }

    }

    @Override
    public void freezeAccount(FreezeAccountParams freezeAccountParams) {
        AccountDbPo accountDb = accountDbPoService.getById(freezeAccountParams.getId());
        TpService service = tpFactory.getService(accountDb.getAccountSupplier());
        service.forceQN(accountDb.getAccountId(), 1);
    }

    @Override
    public void setDiscount(AccountDbReqDto reqDto) {
        AccountDbPo accountDb = accountDbPoService.getById(reqDto.getId());
        if (ObjectUtil.isEmpty(accountDb)) {
            throw new AppException("账号不存在");
        }
        if (!ObjectUtil.equals(accountDb.getStatus(), 1)) {
            throw new AppException("仅支持设置已分配账号折扣");
        }
        if (reqDto.getDiscount() > 20000) {
            throw new AppException("折扣不能大于2.00");
        }
        AccountDbPo po = accountDbReqDtoConvertor.dto2Po(reqDto);
        po.setId(reqDto.getId());
        accountDbPoService.updateById(po);
    }

    @Override
    public void downCloudAccount() {
        // 查询阿里云和腾讯云 主账号配置

        //逐个调用 接口 获取到数据保存

        List<AliConfig.AliAccount> accounts = aliConfig.getAccounts();

        for (AliConfig.AliAccount account : accounts) {

            if (!account.getAccountId().equals("5345113424787251")) {
                continue;
            }
            TpService ali = tpFactory.getService("ali");

            List<AccountDbPo> allAccounts = new ArrayList<>();
            Integer currentPage = 1;
            final Integer pageSize = 20; // 每次查询10条
            boolean hasMoreData = true;

            while (hasMoreData) {
                try {
                    // 分页查询云数据
                    List<AccountDbPo> pageData = ali.queryFromCloud(account.getAccountId(), currentPage, pageSize);



                    if (CollectionUtil.isEmpty(pageData)) {
                        // 没有更多数据，结束循环
                        hasMoreData = false;
                    } else {
                        // 将当前页数据添加到总集合中

                        pageData.forEach(accountDbPo -> {
                            try {
                                GetCreditInfoResponseBody.GetCreditInfoResponseBodyData accountBalance = aliUtil.getAccountBalance(accountDbPo.getAccountId());
                                if (MathUtil.strParseLong(accountBalance.getAvailableCredit()) <= 0) {
                                    return;
                                }
                                allAccounts.add(accountDbPo);
                            } catch (Exception e) {
                                log.error(e.getMessage());
                            }
                        });

                        // 如果返回的数据量小于pageSize，可能是最后一页
                        if (pageData.size() < pageSize) {
                            hasMoreData = false;
                        } else {
                            // 准备查询下一页
                            currentPage++;
                        }
                    }
                } catch (Exception e) {
                    hasMoreData = false; // 发生异常时终止查询
                }
            }

            // 2. 每 100 条一组进行拆分
            List<List<AccountDbPo>> batches = splitIntoBatches(allAccounts, 100);

            // 3. 创建线程池（10 个线程）
            ExecutorService executor = Executors.newFixedThreadPool(10);

            // 4. 提交任务
            for (List<AccountDbPo> batch : batches) {
                executor.submit(() -> {
                    try {
                        accountDbPoService.saveBatch(batch);  // 执行批量保存
                        System.out.println(Thread.currentThread().getName() + " 保存了 " + batch.size() + " 条数据");
                    } catch (Exception e) {
                        System.err.println("保存失败: " + e.getMessage());
                    }
                });
            }

            // 5. 关闭线程池并等待所有任务完成
            executor.shutdown();
            try {
                executor.awaitTermination(1, TimeUnit.HOURS);  // 最多等待 1 小时
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.err.println("任务被中断");
            }

            System.out.println("所有数据保存完成！");

        }

        /*for (TencentConfig.TencentAccount account : tencentConfig.getAccounts()) {
            TpService tencent = tpFactory.getService("tencent");

            List<AccountDbPo> allAccounts = new ArrayList<>();
            Integer currentPage = 1;
            final Integer pageSize = 20; // 每次查询10条
            boolean hasMoreData = true;

            while (hasMoreData) {
                try {
                    // 分页查询云数据
                    List<AccountDbPo> pageData = tencent.queryFromCloud(account.getAccountId(), currentPage, pageSize);



                    if (CollectionUtil.isEmpty(pageData)) {
                        // 没有更多数据，结束循环
                        hasMoreData = false;
                    } else {
                        // 将当前页数据添加到总集合中

                        pageData.forEach(accountDbPo -> {
                            try {

                                JSONObject balanceObject = tencentUtils.queryAccountInfo(account.getAccountId(), accountDbPo.getAccountId());
                                if (balanceObject.getBigDecimal("RemainingCredit").compareTo(BigDecimal.ZERO) <= 0 ) {
                                    return;
                                }

                                allAccounts.add(accountDbPo);
                            } catch (Exception e) {
                                log.error(e.getMessage());
                            }
                        });

                        // 如果返回的数据量小于pageSize，可能是最后一页
                        if (pageData.size() < pageSize) {
                            hasMoreData = false;
                        } else {
                            // 准备查询下一页
                            currentPage++;
                        }
                    }
                } catch (Exception e) {
                    hasMoreData = false; // 发生异常时终止查询
                }
            }
             // 2. 每 100 条一组进行拆分
            List<List<AccountDbPo>> batches = splitIntoBatches(allAccounts, 100);

            // 3. 创建线程池（10 个线程）
            ExecutorService executor = Executors.newFixedThreadPool(10);

            // 4. 提交任务
            for (List<AccountDbPo> batch : batches) {
                executor.submit(() -> {
                    try {
                        accountDbPoService.saveBatch(batch);  // 执行批量保存
                        System.out.println(Thread.currentThread().getName() + " 保存了 " + batch.size() + " 条数据");
                    } catch (Exception e) {
                        System.err.println("保存失败: " + e.getMessage());
                    }
                });
            }

            // 5. 关闭线程池并等待所有任务完成
            executor.shutdown();
            try {
                executor.awaitTermination(1, TimeUnit.HOURS);  // 最多等待 1 小时
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.err.println("任务被中断");
            }

            System.out.println("所有数据保存完成！");
        }*/




    }

    @Override
    public void bindBot(BindAccountParams bindAccountParams) {
        bindAccountParams.getAccountIds().forEach(accountId -> {
            accountDbPoService.update(new LambdaUpdateWrapper<AccountDbPo>()
                    .eq(AccountDbPo::getId, accountId)
                    .set(AccountDbPo::getBotId, bindAccountParams.getBotId())
            );
        });
    }

    @Override
    public void mappingBot(MultipartFile file) {

        for (int i = 0; i < 2; i++) {
            try {
                List<ExcelOrderDb> excelOrderDbs = ExcelUtils.parseExcelToEntityList(file, ExcelOrderDb.class, true, i);

                excelOrderDbs.forEach(excelOrderDb -> {

                    BackgroundUserPo backgroundUserPo = backgroundUserMapper.selectOne(new LambdaQueryWrapper<BackgroundUserPo>()
                            .eq(BackgroundUserPo::getNickname, excelOrderDb.getUserName())
                    );
                    if (Objects.nonNull(backgroundUserPo)) {
                        UserBotPo userBotPo = userBotMapper.selectOne(new LambdaQueryWrapper<UserBotPo>()
                                .eq(UserBotPo::getUserCode, backgroundUserPo.getUserCode())
                        );
                        AccountDbPo accountDbPo = accountDbPoService.getOne(new LambdaQueryWrapper<AccountDbPo>()
                                .eq(AccountDbPo::getAccountId, excelOrderDb.getAccountId())
                                .eq(AccountDbPo::getAccountSupplier, StringUtils.equals(excelOrderDb.getCloudProvider(), "阿里云") ? "ali" : "tencent")
                        );
                        if (!Objects.isNull(accountDbPo)) {
                            accountDbPo.setBotId(userBotPo.getBotId());
                            accountDbPoService.updateById(accountDbPo);
                        }
                    }

                });

            } catch (IOException e) {
                throw new RuntimeException(e);
            }


        }
        

    }

    /**
     * 将大 List 拆分成多个小 List
     */
    private static <T> List<List<T>> splitIntoBatches(List<T> list, int batchSize) {
        List<List<T>> batches = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            batches.add(list.subList(i, Math.min(i + batchSize, list.size())));
        }
        return batches;
    }


}